﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Text;

namespace msdcp
{
    enum TriadState
    {
        Normal,
        Highlighted
    }

    enum Command
    {
        START_SESSION =                 0x01,
        END_SESSION =                   0x02,
        SET_DEBUG_MODE =                0x03,
        CLEAR_DEBUG_MODE =              0x04,
        READ_BUS_NUMBER =               0x05,
        WRITE_BUS_NUMBER =              0x06,
        PAGE_READ =                     0x07,
        PAGE_WRITE =                    0x08,
        EEPROM_READ =                   0x09,
        EEPROM_WRITE =                  0x0a,
        PC_ONLINE =                     0x0b,
        SYNC_TIME =                     0x0c,
        RS485_SWITCH =                  0x0d,
        ERASE_INT_FLASH =               0x10,
        SEND_CONFIG	=   				0x11,
        SEND_INTERVAL_DATA =			0x12,
        UPGRADE =                       0x14,
        UPGRADE_BLOCK =                 0x15,
        UPGRADE_LAST_BLOCK =            0x16,
        DATA_CORRECT =                  0x3E,
        DATA_INCORRECT =                0x3f,
        COMMAND_FALSE =                 0x40,
        COMMAND_TRUE =                  0x80
    }

    enum ResponseStatus
    {
        Wait,
        Received,
        Processing,
        Processed,
        Accepted, 
        Denied,
        Ignored,
        ChecksumError,
        BrokenPackage
    }

    enum Unit : byte
    {
        ID_MSD = 0x5E,
        ID_PC = 0x49
    }

    enum SystemState
    {
        NOT_READY,
        READY,
        INDETERMINATE
    }



    public struct IntF_Filter_Area_t_base
    {
        public byte doors;                      // Количество дверей.
        public byte[] min_in_pass_time;           // Минимальное время прохода в салон (для каждой двери).
        public byte[] min_out_pass_time;          // Минимальное время выхода из салона (для каждой двери).
        public byte[] change_dir_time;            // Минимальный интервал между проходами в разных направлениях (для каждой двери).
        public byte[] shirt_sts0_time;            // Минимальное время пребывания в состоянии S0 (для каждой двери).
        public byte[] filter_flags;               // Флаги активации фильтров (для каждой двери).
        /// Пока 00000xxx, 1 - фильтр активен.
        /// 0000000х - PASS_TIME_CTRL
        /// 000000х0 - PASS_DIR_CTRL
        /// 00000х00 - REVERS_PROTECT
        /// 0000x000 - 
        /// 
        public byte Initialized;

        public IntF_Filter_Area_t_base(IntF_Filter_Area_t_base obj)
        {
            doors = obj.doors;
            min_in_pass_time = new byte[3];
            min_in_pass_time[0] = obj.min_in_pass_time[0];
            min_in_pass_time[1] = obj.min_in_pass_time[1];
            min_in_pass_time[2] = obj.min_in_pass_time[2];

            min_out_pass_time = new byte[3];
            min_out_pass_time[0] = obj.min_out_pass_time[0];
            min_out_pass_time[1] = obj.min_out_pass_time[1];
            min_out_pass_time[2] = obj.min_out_pass_time[2];

            change_dir_time = new byte[3];
            change_dir_time[0] = obj.change_dir_time[0];
            change_dir_time[1] = obj.change_dir_time[1];
            change_dir_time[2] = obj.change_dir_time[2];

            shirt_sts0_time = new byte[3];
            shirt_sts0_time[0] = obj.shirt_sts0_time[0];
            shirt_sts0_time[1] = obj.shirt_sts0_time[1];
            shirt_sts0_time[2] = obj.shirt_sts0_time[2];

            filter_flags = new byte[3];
            filter_flags[0] = obj.filter_flags[0];
            filter_flags[1] = obj.filter_flags[1];
            filter_flags[2] = obj.filter_flags[2];

            Initialized = obj.Initialized;
        }
    }

    struct IntF_Filter_Area_t_182
    {
        public byte doors;                      // Количество дверей.
        public byte[] min_in_pass_time;           // Минимальное время прохода в салон (для каждой двери).
        public byte[] min_out_pass_time;          // Минимальное время выхода из салона (для каждой двери).
        public byte[] change_dir_time;            // Минимальный интервал между проходами в разных направлениях (для каждой двери).
        public byte[] shirt_sts0_time;            // Минимальное время пребывания в состоянии S0 (для каждой двери).
        public byte[] filter_flags;               // Флаги активации фильтров (для каждой двери).
        /// Пока 00000xxx, 1 - фильтр активен.
        /// 0000000х - PASS_TIME_CTRL
        /// 000000х0 - PASS_DIR_CTRL
        /// 00000х00 - REVERS_PROTECT
        /// 0000x000 - 
        public byte[]       max_pass_thru_closed;       // Максимальное количество пассажиров, вышедших при закрытой двери (для каждой двери).
        public byte         Initialized;
        public byte falps;                        // Вектор с количеством створок
        public byte stsDiffTime;//Переменная для настройки чувствительность при перекрестной установке датчиков

        public IntF_Filter_Area_t_182(IntF_Filter_Area_t_182 obj)
        {
            doors = obj.doors;
            min_in_pass_time = new byte[3];
            min_in_pass_time[0] = obj.min_in_pass_time[0];
            min_in_pass_time[1] = obj.min_in_pass_time[1];
            min_in_pass_time[2] = obj.min_in_pass_time[2];

            min_out_pass_time = new byte[3];
            min_out_pass_time[0] = obj.min_out_pass_time[0];
            min_out_pass_time[1] = obj.min_out_pass_time[1];
            min_out_pass_time[2] = obj.min_out_pass_time[2];

            change_dir_time = new byte[3];
            change_dir_time[0] = obj.change_dir_time[0];
            change_dir_time[1] = obj.change_dir_time[1];
            change_dir_time[2] = obj.change_dir_time[2];

            shirt_sts0_time = new byte[3];
            shirt_sts0_time[0] = obj.shirt_sts0_time[0];
            shirt_sts0_time[1] = obj.shirt_sts0_time[1];
            shirt_sts0_time[2] = obj.shirt_sts0_time[2];

            filter_flags = new byte[3];
            filter_flags[0] = obj.filter_flags[0];
            filter_flags[1] = obj.filter_flags[1];
            filter_flags[2] = obj.filter_flags[2];

            max_pass_thru_closed = new byte[3];
            max_pass_thru_closed[0] = obj.max_pass_thru_closed[0];
            max_pass_thru_closed[1] = obj.max_pass_thru_closed[1];
            max_pass_thru_closed[2] = obj.max_pass_thru_closed[2];

            Initialized = obj.Initialized;
            falps = obj.falps;
            stsDiffTime = obj.stsDiffTime;
            
        }
    }

    public enum Protocol
    {
        CRC8_6Byte,
        CRC1_5Byte
    }

    public struct IntF_Service_Area_t
    {
        public UInt32 Signature;
        public UInt16 PoVer;
        public UInt32 NSRAA_Start;
        public UInt32 Next_Data_Area_Address;
        public UInt32 Next_Saved_Records_Area_Address;
        public UInt32 SJ_Start_Address;
        public UInt32 SJ_EndDataArea;
        public String PO_Descriptor;
        public Byte KS;
    }


    // Размер структуры 72 байта.
    public struct ExtF_Service_Area_t
    {
        public UInt32 Signature;//4
        public UInt16 PoVer;//2
        public Byte damaged_flag;//1
        public String car_number;//17
        public UInt32 data_area_begin;//4
        public UInt32 data_len;//4
        //public UInt16 ext_data_len;//1
        public UInt16 transmit_time;//2
        public UInt16 transmit_date;//2
        public Byte saved_status;//1
        public UInt16 FirstRecTime;//2
        public UInt16 FirstRecDate;//2
        public String PO_Descriptor;//30
        public Byte KS;//1

        internal List<byte> ToList()
        {
            List<byte> result = new List<byte>();
            result.AddRange(BitConverter.GetBytes(Signature));
            result.AddRange(BitConverter.GetBytes(PoVer));
            result.Add(damaged_flag);
            Encoding enc = Encoding.UTF8;
            byte[] bn = new byte[Program.BN_STRING_LEN];
            Array.Copy(enc.GetBytes(car_number), bn, car_number.Length);
            result.AddRange(bn);
            result.AddRange(BitConverter.GetBytes(data_area_begin));
            result.AddRange(BitConverter.GetBytes(data_len));
            result.AddRange(BitConverter.GetBytes(transmit_time));
            result.AddRange(BitConverter.GetBytes(transmit_date));
            result.Add(saved_status);
            result.AddRange(BitConverter.GetBytes(FirstRecTime));
            result.AddRange(BitConverter.GetBytes(FirstRecDate));
            byte[] po = new byte[Program.PO_DESCRIPTOR_LEN];
            Array.Copy(enc.GetBytes(PO_Descriptor), po, PO_Descriptor.Length);
            result.AddRange(po);
            result.Add(KS);
            return result;
        }
    }


    public struct BOOTDESC
    {
	    public ushort		PoVer;
	    public ushort		bootFirstPage;
	    public uint		    bootLen;
	    public byte		    force;
	    public byte		    KS;
        public List<byte> ToByteList()
        {
            List<byte> result = new List<byte>();
            result.AddRange(BitConverter.GetBytes(PoVer));
            result.AddRange(BitConverter.GetBytes(bootFirstPage));
            result.AddRange(BitConverter.GetBytes(bootLen));
            result.Add(force);
            result.Add(KS);
            return result;
        }
        public void CalculateChecksum()
        {
            byte CS = 0;
            byte[] pData = this.ToByteList().ToArray();
            int len = pData.Length;

            for (int i = 0; i < (len - 1); ++i)
            {
                CS += pData[i];
            }

            KS = (byte)(0xff - CS + 1);
        }
    }

    [Flags]
    public enum BitFields : byte
    {
        Undefined = 0,
        BU_present = 1,
        connector_STATE = 2,
        coverOpen = 4,
        CheckSupplTime = 8,
        Debug_Mode = 16,
        USART_8_BIT_MODE = 32,
        _8_bit_data_sent = 64
    }

    [Flags]
    public enum System_Status_1
    {
        SYS_STAT_1_NO_ERRORS = 0,
        SYS_STAT_1_DOOR_ERROR_F = 1,
        SYS_STAT_1_DOOR_ERROR_R = 2,
        SYS_STAT_1_DOOR_ERROR_R1 = 4,
        SYS_STAT_1_SENSORS_FAULT_F = 8,
        SYS_STAT_1_SENSORS_FAULT_R = 16,
        SYS_STAT_1_SENSORS_FAULT_R1 = 32
    }

    [Flags]
    public enum SystemErrors
    {
        ERR_NO_ERRORS = 0,
        ERR_FRONT_DOOR_ERROR = 1,
        ERR_REAR_DOOR_ERROR = 2,
        ERR_REAR_1_DOOR_ERROR = 4,
        ERR_FRONT_SENSOR_BLOCKED = 8,
        ERR_REAR_SENSOR_BLOCKED = 16,
        ERR_REAR_1_SENSOR_BLOCKED = 32,
        ERR_MAIN_SUPPLAY_OFF = 64,
        ERR_COVER_IS_OPEN = 128,
        ERR_CONNECTOR_ERROR = 256,
        ERR_FLASH_EJECTED_WO_SAVING = 512,
        ERR_DATA_DOWNLOAD_ERROR = 1024,
        ERR_GRANIT_DISCONNECTED = 2048,
        ERR_CONSOLE_DISCONNECTED = 4096
    }

    [Flags]
    public enum System_Status
    {
        SYS_STAT_UNDEFINED = 0,
        SYS_STAT_FLASH_PRESENT = 1,
        SYS_STAT_FLASH_OCCURED = 2,
        SYS_STAT_NEED_FIX_DWNLD = 4,
        SYS_STAT_TIME_NEED_CHECK = 8,
        SYS_STAT_STOP_STATE = 16,
        SYS_STAT_PIN_TEST_MODE = 32,
        SYS_STAT_INT_FLASH_NEED_FORMAT = 64,
        SYS_STAT_SAVE_EJECT_BUTTON_PRESSED = 128,
        SYS_STAT_GENERATOR_STATE = 256,
        SYS_STAT_NEED_STOP_WRITE_F = 512,
        SYS_STAT_NEED_STOP_WRITE_R = 1024,
        SYS_STAT_NEED_STOP_WRITE_R1 = 2048,
        SYS_STAT_NEED_CHECK_TRACKER = 4096,
        SYS_STAT_NEED_FLASH_REWRITE = 8192
    }


    [Flags]
    public enum Granit_Status
    {
        GS_UNDEFINED = 0,
        GS_DOOR_F_DATA_TO_SEND = 1,
        GS_READY_TO_SEND = 2,
        GS_CONSOLE_PRESENT = 4,
        GS_MESSAGE_TO_SEND = 8,
        GS_ERROR_TO_SEND = 16,
        GS_PC_PRESENT = 32,
        GS_COMMAND_RECEIVED = 64,
        GS_GRANIT_PRESENT = 128
    }

    [Flags]
    public enum System_Message
    {
        SYS_MESS_UNDEFINED = 0,
        SYS_MESS_SAVE_EJECT_BUTTON_PRESSED = 1,
        SYS_MESS_DATE_CHANGED = 2,
        SYS_MESS_NEED_TIME_DATE_SYNC = 4,
        SYS_MESS_DATA_SAVED_CORRECTLY = 8,
        SYS_MESS_NOT_ALL_DATA_SAVED = 16,
        SYS_MESS_FLASH_EJECTED_WITHOUT_SAVING = 32,
        SYS_MESS_ERROR_FLASH_EJECT = 64,
        SYS_MESS_REBOOT = 128,
        SYS_MESS_FLASH_CONNECT_AGANE = 256,
        SYS_MESS_UPDATE_DONE = 512,
        SYS_MESS_BU_CONNECTED = 1024,
        SYS_MESS_NEW_TRIAD = 2048
    }


    public struct _Sys_descriptor_t
    {
        public System_Status System_Status;
        public System_Message System_Message;
        public byte Tracker_sts;
        public Granit_Status Granit_sts;
        public byte ResetSource;
        public byte WhereReset;
        public byte check_try;
        public BitFields bf;
        public byte virtual_door_should_be_closed;
        public byte USART_SPEED;
        public ushort upgrade_time;

        public _Sys_descriptor_t(byte[] buffer)
        {
            System_Status = 0;
            System_Message = 0;
            Tracker_sts = 0;
            Granit_sts = 0;
            ResetSource = 0;
            WhereReset = 0;
            check_try = 0;
            bf = (BitFields)0;
            virtual_door_should_be_closed = 0;
            USART_SPEED = 0;
            upgrade_time = 0xffff;
            try
            {
                System_Status = (System_Status)BitConverter.ToUInt16(buffer, 0);
                System_Message = (System_Message)BitConverter.ToUInt16(buffer, 2);
                Tracker_sts = buffer[4];
                Granit_sts = (Granit_Status)buffer[5];
                ResetSource = buffer[6];
                WhereReset = buffer[7];
                check_try = buffer[8];
                bf = (BitFields)buffer[9];
                virtual_door_should_be_closed = buffer[10];
                USART_SPEED = buffer[11];
                upgrade_time = BitConverter.ToUInt16(buffer, 12);
            }
            catch (Exception ex)
            {
                ErrorReport err = new ErrorReport(ex.Message + ex.StackTrace);
                err.LogName = Program.LOG_NAME;
                err.AddToLog();
            }
        }

        public override string ToString()
        {
            string res = string.Empty;

            res += string.Format("System_Status: {0}{1}", System_Status.ToString(), Environment.NewLine);
            res += string.Format("System_Message: {0}{1}", System_Message.ToString(), Environment.NewLine);
            res += string.Format("Tracker_sts: 0x{0:X2}{1}", Tracker_sts, Environment.NewLine);
            res += string.Format("Granit_sts: {0}{1}", Granit_sts.ToString(), Environment.NewLine);
            res += string.Format("ResetSource: 0x{0:X2}{1}", ResetSource, Environment.NewLine);
            res += string.Format("WhereReset: 0x{0:X2}{1}", WhereReset, Environment.NewLine);
            res += string.Format("check_try: 0x{0:X2}{1}", check_try, Environment.NewLine);
            res += string.Format("Bit fields: {0}{1}", bf.ToString(), Environment.NewLine);
            res += string.Format("virtual_door_should_be_closed: 0x{0:X2}{1}", virtual_door_should_be_closed, Environment.NewLine);
            res += string.Format("USART_SPEED: 0x{0:X2}{1}", USART_SPEED, Environment.NewLine);
            res += string.Format("upgrade_time: 0x{0:X4}{1}", upgrade_time, Environment.NewLine);

            return res;
        }
    }

    static class Program
    {

        public static readonly string LOG_NAME = "msdcpErrorReport.log";
        public static readonly int BLUETOOTH_BAUDRATE = 115200;
        public static readonly int CABLE_BAUDRATE = 38400;
        public static int COMMAND_BYTE = 4;
        public static int DATA_START_BYTE = 5;
        public static int SENDER_BYTE = 1;
        public static int TARGET_BYTE = 0;
        public static int LEN_LSB = 2;
        public static int LEN_MSB = 3;
        public static int PO_DESCRIPTOR_LEN = 30;
        public static ushort MTU_SIZE = 0x1f6; // 502
        public static uint EXT_SIGNATURE = 0xaaaaaaaa;
        public static int PO_VER_SIZE = 2;
        public static int BN_STRING_LEN = 17;
        public static string[] months = { "января", "февраля", 
                                        "марта", "апреля", "мая", 
                                        "июня", "июля", "августа", 
                                        "сентября", "октября", "ноября", "декабря" };
        public static int EXTF_SA_SIZE = 72;
        public static long MAX_JOURNAL_SIZE = 10485760; // Maximum size of journal file, in Bytes (10 MB).
        public static ushort UPGRADE_START_PAGE = 0x06d1; // The number of page from which the upgrade file starts.
        public static int SYS_DESCRIPTOR_LEN = 14;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            if (System.Diagnostics.Debugger.IsAttached == false)
            {
                // Привязываем обработчик к событию потокового исключения.
                // Событие будет обрабатываться только в том случае,
                // если запуск приложения производится без отладчика.
                Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
            }


            ///////////////////////////////////////////////////////
            //
            //          Инициализация конфигурационного файла.
            //

            string configFileName = "msdcp.cfg";      // Имя конфиг файла.
            string initText = "";                   // Начальное содержимое конфиг файла для первого использования.
            // Если конфиг файл не существует (первый запуск, другая версия), 
            // то заполняем содержимое значениями по умолчанию.
            if (!File.Exists(Application.UserAppDataPath + "\\" + configFileName))
            {
                initText = BuildConfig();
            }
            // Вызов статического конструктора класса.
            SettingsProvider.InitSet(configFileName, initText);

            //
            ///////////////////////////////////////////////////////

            try
            {
                Application.Run(new MainForm());
            }
            catch (System.IO.IOException ioe)
            {
                ErrorReport err = new ErrorReport(ioe.Message + ioe.StackTrace);
                err.LogName = "msdcpErrorReport.log";
                err.AddToLog();
                MessageBox.Show(ioe.Message);
            }
            catch (Exception ex)
            {
                if (ex.InnerException != null)
                {
                    ErrorReport err = new ErrorReport(ex.InnerException.Message + ex.InnerException.StackTrace);
                    err.LogName = "msdcpErrorReport.log";
                    err.AddToLog();
                    MessageBox.Show(ex.Message);
                }
                else
                {
                    ErrorReport err = new ErrorReport(ex.Message + ex.StackTrace);
                    err.LogName = "msdcpErrorReport.log";
                    err.AddToLog();
                    MessageBox.Show(ex.Message);
                }
            }
        }


        // Перехват исключений главного потока приложения.
        private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            if (e.Exception.InnerException != null)
            {
                ErrorReport err = new ErrorReport(
                    e.Exception.InnerException.Message + 
                    e.Exception.InnerException.StackTrace);
                err.LogName = "msdcpErrorReport.log";
                err.AddToLog();
                MessageBox.Show(e.Exception.Message);
                //Debugger.Break();
            }
            else
            {
                ErrorReport err = new ErrorReport(
                    e.Exception.Message + e.Exception.StackTrace);
                err.LogName = "msdcpErrorReport.log";
                err.AddToLog();
                MessageBox.Show(e.Exception.Message);
                //Debugger.Break();
            }
        }

        // Инициализация конфигурационного файла значениями по умолчанию.
        private static string BuildConfig()
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            sb.AppendLine("[WindowState]");
            sb.AppendLine(((int)FormWindowState.Normal).ToString());
            sb.AppendLine("[WindowPosition]");
            sb.AppendLine("X=300");
            sb.AppendLine("Y=100");
            sb.AppendLine("[WindowDimensions]");
            sb.AppendLine("Width=580");
            sb.AppendLine("Height=650");
            sb.AppendLine("Column1=10");
            sb.AppendLine("Column2=5");
            sb.AppendLine("Column3=75");
            sb.AppendLine("Column4=20");
            sb.AppendLine("[LastUsedComPort]");
            sb.AppendLine("COM1");
            sb.AppendLine("[BaudRate]");
            sb.AppendLine("38400");
            sb.AppendLine("[EndOfFile]");
            return sb.ToString();
        }

    }
}
